/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "lwip.h"
#include "sdmmc.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdbool.h>
#include <stdio.h>
#include <string.h>

#include "My_InFlash.h"
#include "My_Spi.h"
#include "retarget.h"
#include "tcp.h"
#include "My_TCP.h"
#include "My_uart.h"
#include "My_Wifi.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
// 定义STM32H7所有RAM区域的起始和结束地址（从链接脚本提取）
#define ITCMRAM_START  0x00000000
#define ITCMRAM_END    0x00010000  // 0x00000000 + 64K
#define DTCMRAM_START  0x20000000
#define DTCMRAM_END    0x20020000  // 0x20000000 + 128K
#define RAM_D1_START   0x24000000
#define RAM_D1_END     0x24080000  // 0x24000000 + 512K
#define RAM_D2_START   0x30000000
#define RAM_D2_END     0x30048000  // 0x30000000 + 288K
#define RAM_D3_START   0x38000000
#define RAM_D3_END     0x38010000  // 0x38000000 + 64K


/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MPU_Config(void);
/* USER CODE BEGIN PFP */


void jump_to_application(uint32_t address) {
    // 读取应用程序向量表里的 MSP 与 ResetHandler
    uint32_t app_msp = *(__IO uint32_t*)address;
    uint32_t app_reset = *(__IO uint32_t*)(address + 4);

    // 调试输出（跳转前打印向量表，帮助定位问题）
    printf("JumpTry: APP_ADDR=0x%08X, MSP=0x%08X, RESET=0x%08X\r\n", address, app_msp, app_reset);

    // 检查 MSP 是否在任一有效 RAM 区间 —— 注意右开区间 (<= 改为 <)
    bool valid_sp = (app_msp >= ITCMRAM_START && app_msp <= ITCMRAM_END) ||
        (app_msp >= DTCMRAM_START && app_msp <= DTCMRAM_END) ||
        (app_msp >= RAM_D1_START && app_msp <= RAM_D1_END) ||
        (app_msp >= RAM_D2_START && app_msp <= RAM_D2_END) ||
        (app_msp >= RAM_D3_START && app_msp <= RAM_D3_END);
    if (!valid_sp) {
        printf("Invalid MSP, abort jump.\r\n");
        return;
    }

    // 禁止中断
    __disable_irq();

    // 可选：把 HAL 清理一次（关闭外设，重置 RCC）
    HAL_RCC_DeInit();
    HAL_DeInit();

    // 关闭 SysTick 避免留下中断
    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;
    NVIC_SystemReset; // 不执行重置，只是确保不使用 SysTick 中断

    // 关闭所有 NVIC 中断使能并清挂起（更彻底）
    for (int i = 0; i < 8; i++) {
        NVIC->ICER[i] = 0xFFFFFFFF;
        NVIC->ICPR[i] = 0xFFFFFFFF;
    }

    // D-Cache / I-Cache 操作（CMSIS 提供）
#if defined(SCB_CleanInvalidateDCache)
    SCB_CleanInvalidateDCache();
    SCB_DisableDCache();
#endif
#if defined(SCB_InvalidateICache)
    SCB_InvalidateICache();
    SCB_DisableICache();
#endif

    // 数据同步与指令同步屏障，确保缓存操作完成
    __DSB();
    __ISB();

    // 设置向量表基址（APP 的 Flash 起始地址）
    SCB->VTOR = address;

    // 设置 MSP（主堆栈指针）
    __set_MSP(app_msp);

    // 确保 Reset handler 地址的 Thumb 位为 1（地址最低位必须为 1）
    uint32_t jump_addr = (app_reset & (~(uint32_t)1)) | 1U;
    typedef void (*pFunc)(void);
    pFunc app_reset_handler = (pFunc)jump_addr;

    printf("Jumping to 0x%08X (thumb)\r\n", jump_addr);

    // 确保所有指令已完成，随后跳转
    __DSB();
    __ISB();
    app_reset_handler();
    // 如果跳回来（理论上不会），就再断言进入死循环
    while (1) { ; }
}

static uint8_t update_result = 0;
static uint8_t update_begin = 0;
static uint8_t update_block = 0;

void update_complete_handler(uint8_t result) {
    if (result == 0x00) {
        update_result = 1;
        printf("固件升级成功！\r\n");
    } else {
        update_result = 2;
        printf("固件升级失败！\r\n");
    }
}

void update_begin_handler(uint8_t result) {
    if (result == 0x01) {
        update_begin = 1;
        printf("开始升级！\r\n");
    }
}

void update_Block_handler(uint8_t result) {
    if (result == 0x01) {
        update_block = 1;
        printf("一块接收完毕，刷新等待时间！\r\n");
    }
}

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void) {
    /* USER CODE BEGIN 1 */

    /* USER CODE END 1 */

    /* MPU Configuration--------------------------------------------------------*/
    MPU_Config();

    /* MCU Configuration--------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* USER CODE BEGIN Init */

    /* USER CODE END Init */

    /* Configure the system clock */
    SystemClock_Config();

    /* USER CODE BEGIN SysInit */
    HAL_Delay(500);
    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_DMA_Init();

    MX_SDMMC1_SD_Init();
    MX_USART1_UART_Init();
    MX_USART2_UART_Init();
    /* USER CODE BEGIN 2 */
    RetargetInit(&huart2);
    firmware_upgrade_Init();
    My_Spi_Init();
    MX_LWIP_Init();
    HAL_GPIO_WritePin(GPIOH,GPIO_PIN_10, GPIO_PIN_SET);

    HAL_GPIO_WritePin(GPIOH,GPIO_PIN_11, GPIO_PIN_SET);
    uint8_t isUpdata_Flag = 1;
    uint8_t RE_Ip[4] = {192, 168, 5, 39};
    tcp_client_init(RE_Ip, 1233, 1234);
    //tcp注册回调
    set_update_Begin_callback(update_begin_handler);
    set_update_Block_callback(update_Block_handler);
    set_update_complete_callback(update_complete_handler);
    //串口注册回调
    set_update_uart_Begin_callback(update_begin_handler);
    set_update_uart_Block_callback(update_Block_handler);
    set_update_uart_complete_callback(update_complete_handler);
    //spi注册回调
    set_update_Spi_Begin_callback(update_begin_handler);
    set_update_Spi_Block_callback(update_Block_handler);
    set_update_Spi_complete_callback(update_complete_handler);
    uint8_t cnt_T = 0;
    uint8_t cnt_T1 = 0;


    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1) {
        if (!update_begin) {
            //0 不升级状态
            if (cnt_T > 50) {
                isUpdata_Flag = 0; //10s没反应，不升级，直接跳转
                cnt_T = 0;
            }
            cnt_T++;
        } else {
            if (update_block) {
                update_block = 0;
                cnt_T1 = 0; //清除升级时等待时间
            }
            if (cnt_T1 > 150) {
                //接收到升级标志（头文件）
                isUpdata_Flag = 0; //30s没反应，升级失败，直接跳转
                cnt_T1 = 0;
            }
            cnt_T1++;
        }
        firmware_upgrade_Loop();
        MX_LWIP_Process();
        Updata_loop();
        // 通过参数区参数进行跳转：参数区格式如下 ：
        // 0xaa 0x55 0xaa 0x55 为有数据
        // 不擦除 0xA5代表app1和app2为同一代码
        // 0xAA代表app1为旧版，app2为升级失败新版本
        // 0XBB代表app1为旧版，app2为升级成功新版本
        if ((update_result == 1) || (update_result == 2) || (!isUpdata_Flag)) {
            uint32_t My_Dat_Flag = *(uint32_t*)My_Date;
            HAL_Delay(200);
            //擦掉，标记区
            InternalFlash_Erase(My_Date, 128 * 1024, 1);
            if ((My_Dat_Flag == 0x55AA55AA) && (!isUpdata_Flag)) {
                printf("不升级进入\r\n");
                //这种条件下进入是不升级进入
                uint8_t tmep[32] = {0xaa, 0x55, 0xaa, 0x55, 0xa5};
                InFlash_Write(My_Date, tmep, 5);
            } else if ((My_Dat_Flag == 0x55AA55AA) && (update_result == 2)) {
                //这种条件下进入是升级失败进入
                printf("升级错误进入\r\n");
                uint8_t tmep[32] = {0xaa, 0x55, 0xaa, 0x55, 0xAA};
                InFlash_Write(My_Date, tmep, 5);
            } else if ((My_Dat_Flag == 0x55AA55AA) && (update_result == 1)) {
                //这种条件下进入是升级成功进入
                printf("升级进入\r\n");
                uint8_t tmep[32] = {0xaa, 0x55, 0xaa, 0x55, 0xBB};
                InFlash_Write(My_Date, tmep, 5);
                HAL_Delay(100);
                InternalFlash_Erase(APP1, 256 * 1024, 2);
                HAL_Delay(100);
                for (uint8_t i = 0; i < 8; i++) {
                    uint8_t* tmep = (uint8_t*)(APP2 + (i * 32 * 1024));
                    InFlash_Write(APP1 + (i * 32 * 1024), tmep, 32 * 1024);
                }
            } else {
                //没有My_Dat_Flag数据，写入
                uint8_t tmep[32] = {0xaa, 0x55, 0xaa, 0x55, 0xa5};
                InFlash_Write(My_Date, tmep, 5);
            }
            HAL_Delay(200);
            InternalFlash_Erase(APP_RUN, 256 * 1024, 1);
            for (uint8_t i = 0; i < 8; i++) {
                uint8_t* tmep = (uint8_t*)(APP1 + (i * 32 * 1024));
                InFlash_Write(APP_RUN + (i * 32 * 1024), tmep, 32 * 1024);
            }
            printf("跳转开始！！！！！！！！！\r\n");
            HAL_Delay(200);
            jump_to_application(APP_RUN);
        }
        HAL_Delay(200);
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
    }
    /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /** Supply configuration update enable
    */
    HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);

    /** Configure the main internal regulator output voltage
    */
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);

    while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {
    }

    /** Initializes the RCC Oscillators according to the specified parameters
    * in the RCC_OscInitTypeDef structure.
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 5;
    RCC_OscInitStruct.PLL.PLLN = 192;
    RCC_OscInitStruct.PLL.PLLP = 2;
    RCC_OscInitStruct.PLL.PLLQ = 4;
    RCC_OscInitStruct.PLL.PLLR = 2;
    RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
    RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    RCC_OscInitStruct.PLL.PLLFRACN = 0;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
        Error_Handler();
    }

    /** Initializes the CPU, AHB and APB buses clocks
    */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
        | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2
        | RCC_CLOCKTYPE_D3PCLK1 | RCC_CLOCKTYPE_D1PCLK1;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) {
        Error_Handler();
    }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/* MPU Configuration */

void MPU_Config(void) {
    MPU_Region_InitTypeDef MPU_InitStruct = {0};

    /* Disables the MPU */
    HAL_MPU_Disable();

    /** Initializes and configures the Region and the memory to be protected
    */
    MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    MPU_InitStruct.Number = MPU_REGION_NUMBER0;
    MPU_InitStruct.BaseAddress = 0x30004000;
    MPU_InitStruct.Size = MPU_REGION_SIZE_256B;
    MPU_InitStruct.SubRegionDisable = 0x0;
    MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
    MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
    MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /** Initializes and configures the Region and the memory to be protected
    */
    MPU_InitStruct.Number = MPU_REGION_NUMBER1;
    MPU_InitStruct.BaseAddress = 0x30044000;
    MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;
    MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    /* Enables the MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void) {
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
    __disable_irq();
    while (1) {
    }
    /* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line) {
    /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number,
       ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
